#include "StdAfx.h"

#include "AIModuleWrapper.hpp"
#include "LoaderSection.hpp"

#include "ManagedAIModule.hpp"
#include "ManagedGame.hpp"
#include "ManagedPlayer.hpp"
#include "ManagedPosition.hpp"
#include "ManagedUnit.hpp"


using namespace System::Reflection;

using namespace System;
using namespace System::IO;
using namespace System::Configuration;

AIModuleWrapper::AIModuleWrapper(BWAPI::Game *Game) {

	Assembly ^Me = Assembly::GetExecutingAssembly();
	String ^ConfigPath = Path::ChangeExtension(Me->Location, "config");

	WriteLog("Loader: Initializing");
	WriteLog("Loader: Loading configuration file \"{0}\"", ConfigPath);
	if (!File::Exists(ConfigPath)) {
		WriteLog("Loader: Could not find configuration file");
		return;
	}
	
	String ^AssemblyName = nullptr, ^ClassName = nullptr;
	int InWrapperSection = 0;
	int LineNum = 0;
	
	ExeConfigurationFileMap ^ConfigMap = gcnew ExeConfigurationFileMap();
	ConfigMap->ExeConfigFilename = ConfigPath;
	System::Configuration::Configuration ^Config =
			ConfigurationManager::OpenMappedExeConfiguration(
					ConfigMap, ConfigurationUserLevel::None);
	BWAPI::Managed::Configuration::LoaderSection ^Loader =
			static_cast<BWAPI::Managed::Configuration::LoaderSection ^>(
					Config->Sections["Loader"]);
	if (!Loader) {
		WriteLog("Loader: Could not find Loader section in configuration file");
		return;
	}
	try {
		m_AIModule = static_cast<BWAPI::Managed::AIModule ^>(
				Activator::CreateInstance(Loader->ClassType, 
						gcnew array<Object ^> {
								Config, gcnew BWAPI::Managed::Game(Game) }));
	} catch (Exception ^Ex) {
		WriteLog("Loader: Unable to instantiate an instance of {1}{0}",
				Environment::NewLine, Ex);
		return;
	}
}

AIModuleWrapper::~AIModuleWrapper(void) {
	delete m_AIModule;
}

void AIModuleWrapper::onStart(void) {
	m_GameStarted = true;
	if (m_AIModule) {
		try {
			m_AIModule->OnStart();
		} catch (Exception ^Ex) {
			WriteLog("{1}: Exception thrown in {1}.OnStart({2}): {0}{3}",
					Environment::NewLine, m_AIModule->GetType()->FullName,
					void::typeid->FullName, Ex->ToString());
		}
	} else {
		/*BWAPI::Broodwar->printf("AI failed to load. Please refer to log file for "
				"details");*/
	}
}

void AIModuleWrapper::onEnd(bool IsWinner) {
	if (m_AIModule) {
		try {
			m_AIModule->OnEnd(IsWinner);
		} catch (Exception ^Ex) {
			WriteLog("{1}: Exception thrown in {1}.OnEnd({2}): {0}{3}",
					Environment::NewLine, m_AIModule->GetType()->FullName,
					bool::typeid->FullName, Ex->ToString());
		}
	}
}

void AIModuleWrapper::onFrame(void) {
	if (m_AIModule) {
		try {
			m_AIModule->OnFrame();
		} catch (Exception ^Ex) {
			WriteLog("{1}: Exception thrown in {1}.OnFrame({2}): {0}{3}",
					Environment::NewLine, m_AIModule->GetType()->FullName,
					void::typeid->FullName, Ex->ToString());
		}
	}
}


bool AIModuleWrapper::onSendText(std::string Text) {
	if (m_AIModule) {
		try {
			return m_AIModule->OnSendText(gcnew String(Text.c_str()));
		} catch (Exception ^Ex) {
			WriteLog("{1}: Exception thrown in {1}.OnSendText({2}): {0}{3}",
					Environment::NewLine, m_AIModule->GetType()->FullName,
					String::typeid->FullName, Ex->ToString());

		}
	}
	return true;
}

void AIModuleWrapper::onPlayerLeft(BWAPI::Player *Player) {
	if (m_AIModule) {
		try {
			m_AIModule->OnPlayerLeft(gcnew BWAPI::Managed::Player(Player));
		} catch (Exception ^Ex) {
			WriteLog("{1}: Exception thrown in {1}.OnPlayerLeft({2}): {0}{3}",
					Environment::NewLine, m_AIModule->GetType()->FullName,
					BWAPI::Managed::Player::typeid, Ex->ToString());
		}
	}
}


void AIModuleWrapper::onNukeDetect(BWAPI::Position Target) {
	if (m_AIModule) {
		try {
			m_AIModule->OnNukeDetect(BWAPI::Managed::Position(Target));
		} catch (Exception ^Ex) {
			WriteLog("{1}: Exception thrown in {1}.OnNukeDetect({2}): {0}{3}",
					Environment::NewLine, m_AIModule->GetType()->FullName,
					BWAPI::Managed::Position::typeid, Ex->ToString());
		}
	}
}

void AIModuleWrapper::onUnitCreate(BWAPI::Unit *Unit) {
	if (m_AIModule) {
		try {
			m_AIModule->OnUnitCreate(gcnew BWAPI::Managed::Unit(Unit));
		} catch (Exception ^Ex) {
			WriteLog("{1}: Exception thrown in {1}.OnUnitCreate({2}): {0}{3}",
					Environment::NewLine, m_AIModule->GetType()->FullName,
					BWAPI::Managed::Unit::typeid, Ex->ToString());
		}
	}
}

void AIModuleWrapper::onUnitDestroy(BWAPI::Unit *Unit) {
	if (m_AIModule) {
		try {
			m_AIModule->OnUnitDestroy(gcnew BWAPI::Managed::Unit(Unit));
		} catch (Exception ^Ex) {
			WriteLog("{1}: Exception thrown in {1}.OnUnitDestroy({2}): {0}{3}",
					Environment::NewLine, m_AIModule->GetType()->FullName,
					BWAPI::Managed::Unit::typeid, Ex->ToString());
		}
	}
}

void AIModuleWrapper::onUnitMorph(BWAPI::Unit *Unit) {
	if (m_AIModule) {
		try {
			m_AIModule->OnUnitMorph(gcnew BWAPI::Managed::Unit(Unit));
		} catch (Exception ^Ex) {
			WriteLog("{1}: Exception thrown in {1}.OnUnitMorph({2}): {0}{3}",
					Environment::NewLine, m_AIModule->GetType()->FullName,
					BWAPI::Managed::Unit::typeid, Ex->ToString());
		}
	}
}

void AIModuleWrapper::onUnitShow(BWAPI::Unit *Unit) {
	if (m_AIModule) {
		try {
			m_AIModule->OnUnitShow(gcnew BWAPI::Managed::Unit(Unit));
		} catch (Exception ^Ex) {
			WriteLog("{1}: Exception thrown in {1}.OnUnitShow({2}): {0}{3}",
					Environment::NewLine, m_AIModule->GetType()->FullName,
					BWAPI::Managed::Unit::typeid, Ex->ToString());
		}
	}
}

void AIModuleWrapper::onUnitHide(BWAPI::Unit *Unit) {
	if (m_AIModule) {
		try {
			m_AIModule->OnUnitHide(gcnew BWAPI::Managed::Unit(Unit));
		} catch (Exception ^Ex) {
			WriteLog("{1}: Exception thrown in {1}.OnUnitHide({2}): {0}{3}",
					Environment::NewLine, m_AIModule->GetType()->FullName,
					BWAPI::Managed::Unit::typeid, Ex->ToString());
		}
	}
}

void AIModuleWrapper::onUnitRenegade(BWAPI::Unit *Unit) {
	if (m_AIModule) {
		try {
			m_AIModule->OnUnitRenegade(gcnew BWAPI::Managed::Unit(Unit));
		} catch (Exception ^Ex) {
			WriteLog("{1}: Exception thrown in {1}.OnUnitRenegade({2}): {0}{3}",
					Environment::NewLine, m_AIModule->GetType()->FullName,
					BWAPI::Managed::Unit::typeid, Ex->ToString());
		}
	}
}

void AIModuleWrapper::WriteLog(String ^Message) {
	Assembly ^Me = Assembly::GetExecutingAssembly();
	String ^LogPath = Path::ChangeExtension(Me->Location, "log");
	
	StreamWriter ^Log = File::AppendText(LogPath);
	String ^Msg = String::Format("[{0}] {1}", DateTime::Now, Message);
	Log->WriteLine(Msg);
	delete Log;
	Console::WriteLine(Msg);
	if (m_GameStarted) {
		using System::Runtime::InteropServices::Marshal;
		char *str = static_cast<char *>(
				Marshal::StringToHGlobalAnsi(Msg).ToPointer());
		//BWAPI::Broodwar->printf(str);
		Marshal::FreeHGlobal(IntPtr(str));
	}
	
}

void AIModuleWrapper::WriteLog(String ^Message, ... array<Object ^> ^Params) {
	WriteLog(String::Format(Message, Params));
}